
import { _decorator, Component, director, Node } from 'cc';
import { TileMapManager } from '../Tile/TileMapManager';
import { createUINode } from '../../Utils';
import Levels, { ILevel } from '../../Levels';
import DataManager, { IRecord } from '../../Runtime/DataManager';
import { TILE_WIDTH, TILE_HEIGHT } from '../Tile/TileManager';
import { DIRECTION_ENUM, ENTITY_STATE_ENUM, ENTITY_TYPE_ENUM, EVENT_ENUM, SCENE_ENUM } from '../../Enums';
import EventManager from '../../Runtime/EventManager';
import { PlayerManager } from '../Player/PlayerManager';
import { WoodenSkeletonManager } from '../WoodenSkeleton/WoodenSkeletonManager';
import { DoorManager } from '../Door/DoorManager';
import { IronSkeletonManager } from '../IronSkeleton/IronSkeletonManager';
import { BurstManager } from '../Burst/BurstManager';
import { SpikesManager } from '../Spikes/SpikesManager';
import { SmokeManager } from '../Smoke/SmokeManager';
import FaderManager from '../../Runtime/FaderManager';
import { ShakeManager } from '../UI/ShakeManager';
const { ccclass, property } = _decorator;

@ccclass('BattleManager')
export class BattleManager extends Component {
    level: ILevel
    stage: Node
    private smokeLayer: Node
    private inited: boolean = false

    onLoad() {
        DataManager.Instance.levelIndex = 1
        EventManager.Instance.on(EVENT_ENUM.NEXT_LEVEL, this.nextLevel, this)
        EventManager.Instance.on(EVENT_ENUM.PLAYER_MOVE_END, this.checkArrived, this)
        EventManager.Instance.on(EVENT_ENUM.SHOW_SMOKE, this.generateSmoke, this)
        EventManager.Instance.on(EVENT_ENUM.RECORD_STEP, this.record, this)
        EventManager.Instance.on(EVENT_ENUM.REVOKE_STEP, this.revoke, this)
        EventManager.Instance.on(EVENT_ENUM.RESTART_LEVEL, this.initLevel, this)
        EventManager.Instance.on(EVENT_ENUM.OUT_BATTLE, this.outBattle, this)

    }

    onDestroy() {
        EventManager.Instance.off(EVENT_ENUM.NEXT_LEVEL, this.nextLevel)
        EventManager.Instance.off(EVENT_ENUM.PLAYER_MOVE_END, this.checkArrived)
        EventManager.Instance.off(EVENT_ENUM.SHOW_SMOKE, this.generateSmoke)
        EventManager.Instance.off(EVENT_ENUM.RECORD_STEP, this.record)
        EventManager.Instance.off(EVENT_ENUM.REVOKE_STEP, this.revoke)
        EventManager.Instance.off(EVENT_ENUM.RESTART_LEVEL, this.initLevel)
        EventManager.Instance.off(EVENT_ENUM.OUT_BATTLE, this.outBattle)
    }

    start() {
        this.generateStage()
        this.initLevel()
    }

    async initLevel() {
        const level = Levels[`level${DataManager.Instance.levelIndex}`]
        if (level) {
            if(this.inited) {
                await FaderManager.Instance.fadeIn() // 游戏开始加载资源时先遮住画面
            } else {
                await FaderManager.Instance.mask()
            }

            this.clearLevel()
            this.level = level
            DataManager.Instance.mapInfo = level.mapInfo
            DataManager.Instance.mapRowCount = level.mapInfo.length || 0
            DataManager.Instance.mapColumnCount = level.mapInfo[0].length || 0
            await Promise.all([
                this.generateTileMap(),
                this.generateBursts(),
                this.generateSpikes(),
                this.generateSmokeLayer(),
                this.generateDoor(),
                this.generateEnemies(),
            ])
            // 保证人物最后生成
            await this.generatePlayer()

            await FaderManager.Instance.fadeOut()
            this.inited = true
        } else {
            // 没有关卡了，应该有一个游戏结束页面，这里直接退出到主界面
            this.outBattle()
        }
    }

    async outBattle() {
        await FaderManager.Instance.fadeIn()
        director.loadScene(SCENE_ENUM.Start)
    }

    nextLevel() {
        DataManager.Instance.levelIndex++
        this.initLevel()
    }

    // 清除当前关卡数据
    // 主要是清除 stage 下的所有子节点，并重置 DataManager 中的地图信息
    clearLevel() {
        this.stage.destroyAllChildren()
        DataManager.Instance.reset()
    }

    generateStage() {
        this.stage = createUINode()
        this.stage.setParent(this.node)
        this.stage.addComponent(ShakeManager) // 添加震动组件
    }

    async generateTileMap() {
        const tileMap = createUINode()
        tileMap.setParent(this.stage)
        const tileMapManager = tileMap.addComponent(TileMapManager)
        await tileMapManager.init()

        this.adaptPosition()
    }

    async generatePlayer() {
        const player = createUINode()
        player.setParent(this.stage)
        const playerManager = player.addComponent(PlayerManager)
        // await playerManager.init({
        //     x: 2,
        //     y: 8,
        //     type: ENTITY_TYPE_ENUM.PLAYER,
        //     direction: DIRECTION_ENUM.TOP,
        //     state: ENTITY_STATE_ENUM.IDLE
        // })
        await playerManager.init(this.level.player)
        DataManager.Instance.player = playerManager
        // 敌人可能生成比玩家晚，所以在这里触发一次玩家生成时间
        EventManager.Instance.emit(EVENT_ENUM.PLAYER_BORN, true)
    }

    async generateEnemies() {
        const promises = []
        this.level.enemies.forEach((enemy) => {
            const node = createUINode()
            node.setParent(this.stage)
            const Manager = enemy.type === ENTITY_TYPE_ENUM.SKELETON_WOODEN ? WoodenSkeletonManager : IronSkeletonManager
            const manager = node.addComponent(Manager)
            promises.push(manager.init(enemy))
            DataManager.Instance.enemies.push(manager)
        })
        await Promise.all(promises)
        /* 
        const enemy1 = createUINode()
        enemy1.setParent(this.stage)
        const woodenSkeletonManager = enemy1.addComponent(WoodenSkeletonManager)
        await woodenSkeletonManager.init({
            // x: 7,
            // y: 7,
            x: 2,
            y: 4,
            type: ENTITY_TYPE_ENUM.SKELETON_WOODEN,
            direction: DIRECTION_ENUM.TOP,
            state: ENTITY_STATE_ENUM.IDLE
        })
        DataManager.Instance.enemies.push(woodenSkeletonManager)

        const enemy2 = createUINode()
        enemy2.setParent(this.stage)
        const ironSkeletonManager = enemy2.addComponent(IronSkeletonManager)
        await ironSkeletonManager.init({
            // x: 7,
            // y: 7,
            x: 2,
            y: 2,
            type: ENTITY_TYPE_ENUM.SKELETON_IRON,
            direction: DIRECTION_ENUM.TOP,
            state: ENTITY_STATE_ENUM.IDLE
        })
        DataManager.Instance.enemies.push(ironSkeletonManager)
        */
    }

    async generateDoor() {
        const door = createUINode()
        door.setParent(this.stage)
        const doorManager = door.addComponent(DoorManager)
        await doorManager.init(this.level.door)
        // await doorManager.init({
        //     x: 7,
        //     y: 8,
        //     type: ENTITY_TYPE_ENUM.DOOR,
        //     direction: DIRECTION_ENUM.TOP,
        //     state: ENTITY_STATE_ENUM.IDLE
        // })
        DataManager.Instance.door = doorManager
    }

    async generateBursts() {
        const promises = []
        this.level.bursts.forEach((burst) => {
            const node = createUINode()
            node.setParent(this.stage)
            const burstManager = node.addComponent(BurstManager)
            promises.push(burstManager.init(burst))
            DataManager.Instance.bursts.push(burstManager)
        })
        await Promise.all(promises)
    }

    async generateSpikes() {
        const promises = []
        this.level.spikes.forEach((spikes) => {
            const node = createUINode()
            node.setParent(this.stage)
            const spikesManager = node.addComponent(SpikesManager)
            promises.push(spikesManager.init(spikes))
            DataManager.Instance.spikes.push(spikesManager)
        })
        await Promise.all(promises)
    }

    async generateSmoke(x: number, y: number, direction: DIRECTION_ENUM) {
        // 如果烟雾已经存在且处于死亡状态，则复用它，否则创建一个新的烟雾实例
        const item = DataManager.Instance.smokes.find((smoke: SmokeManager) => smoke.state === ENTITY_STATE_ENUM.DEATH)
        if (item) {
            item.x = x
            item.y = y
            item.direction = direction
            item.state = ENTITY_STATE_ENUM.IDLE
            item.node.setPosition(item.x * TILE_WIDTH - TILE_WIDTH * 1.5, -item.y * TILE_HEIGHT + TILE_HEIGHT * 1.5)
        } else {
            const node = createUINode()
            node.setParent(this.smokeLayer)
            const smokeManager = node.addComponent(SmokeManager)
            await smokeManager.init({
                x,
                y,
                direction,
                state: ENTITY_STATE_ENUM.IDLE,
                type: ENTITY_TYPE_ENUM.SMOKE,
            })
            DataManager.Instance.smokes.push(smokeManager)
        }
    }

    async generateSmokeLayer() {
        this.smokeLayer = createUINode()
        this.smokeLayer.setParent(this.stage)
    }

    // 检查玩家是否到达门的位置
    checkArrived() {
        if (!DataManager.Instance.door || !DataManager.Instance.player) return

        const { x: doorX, y: doorY, state: doorState } = DataManager.Instance.door
        const { x: playerX, y: playerY } = DataManager.Instance.player
        if (doorX === playerX && doorY === playerY && doorState === ENTITY_STATE_ENUM.DEATH) {
            EventManager.Instance.emit(EVENT_ENUM.NEXT_LEVEL)
        }
    }

    // 调整 stage 位置，使其居中
    adaptPosition() {
        const { mapRowCount, mapColumnCount } = DataManager.Instance
        const disX = (TILE_WIDTH * mapRowCount) / 2
        const disY = (TILE_WIDTH * mapColumnCount) / 2 + 80
        this.stage.getComponent(ShakeManager).stop()
        this.stage.setPosition(-disX, disY)
    }

    record() {
        const item: IRecord = {
            player: {
                x: DataManager.Instance.player.targetX,
                y: DataManager.Instance.player.targetY,
                state:
                    DataManager.Instance.player.state === ENTITY_STATE_ENUM.IDLE ||
                        DataManager.Instance.player.state === ENTITY_STATE_ENUM.DEATH ||
                        DataManager.Instance.player.state === ENTITY_STATE_ENUM.AIR_DEATH
                        ? ENTITY_STATE_ENUM.IDLE
                        : DataManager.Instance.player.state,
                direction: DataManager.Instance.player.direction,
                type: DataManager.Instance.player.type,
            },
            door: {
                x: DataManager.Instance.door.x,
                y: DataManager.Instance.door.y,
                state: DataManager.Instance.door.state,
                direction: DataManager.Instance.door.direction,
                type: DataManager.Instance.door.type,
            },
            enemies: DataManager.Instance.enemies.map(({ x, y, state, direction, type }) => {
                return { x, y, state, direction, type }
            }),
            spikes: DataManager.Instance.spikes.map(({ x, y, count, type }) => {
                return { x, y, count, type }
            }),
            bursts: DataManager.Instance.bursts.map(({ x, y, state, direction, type }) => {
                return { x, y, state, direction, type }
            }),
        }

        DataManager.Instance.records.push(item)
    }

    revoke() {
        const record = DataManager.Instance.records.pop()
        if (record) {
            DataManager.Instance.player.x = DataManager.Instance.player.targetX = record.player.x
            DataManager.Instance.player.y = DataManager.Instance.player.targetY = record.player.y
            DataManager.Instance.player.state = record.player.state
            DataManager.Instance.player.direction = record.player.direction

            for (let i = 0; i < record.enemies.length; i++) {
                const enemy = record.enemies[i]
                DataManager.Instance.enemies[i].x = enemy.x
                DataManager.Instance.enemies[i].y = enemy.y
                DataManager.Instance.enemies[i].state = enemy.state
                DataManager.Instance.enemies[i].direction = enemy.direction
            }

            for (let i = 0; i < record.bursts.length; i++) {
                const burst = record.bursts[i]
                DataManager.Instance.bursts[i].x = burst.x
                DataManager.Instance.bursts[i].y = burst.y
                DataManager.Instance.bursts[i].state = burst.state
            }

            for (let i = 0; i < record.spikes.length; i++) {
                const spike = record.spikes[i]
                DataManager.Instance.spikes[i].x = spike.x
                DataManager.Instance.spikes[i].y = spike.y
                DataManager.Instance.spikes[i].count = spike.count
            }

            DataManager.Instance.door.x = record.door.x
            DataManager.Instance.door.y = record.door.y
            DataManager.Instance.door.state = record.door.state
            DataManager.Instance.door.direction = record.door.direction
        } else {
            //TODO 播放游戏音频嘟嘟嘟
        }
    }
}
